package com.d4a5.mapping.esri
{
	
	import com.d4a5.mapping.core.MinMax;
	import com.esri.ags.layers.ArcGISDynamicMapServiceLayer;
	import com.esri.ags.symbol.Symbol;
	
	import flash.display.GradientType;
	import flash.display.InterpolationMethod;
	import flash.display.SpreadMethod;
	import flash.filters.DropShadowFilter;
	import flash.geom.Matrix;
	
	import mx.containers.Canvas;
	import mx.controls.Label;
	import mx.events.CollectionEvent;
	import mx.formatters.CurrencyFormatter;
	import mx.formatters.NumberFormatter;

	
	public class wmeGradientLegend extends Canvas
	{
		
		protected var _refreshRequested:Boolean = false;
		
		protected var _visibleRange:MinMax;
		protected var _layer:wmeGraphicsLayer;
		
		public var minLabel:Label = new Label();
		public var maxLabel:Label = new Label();
		
		public var isCurrency:Boolean = false;
		public var isPercentage:Boolean = false;
		public var isNegative:Boolean = false;
	
		public var calculatePercentageMethod:int = 0;
		public var showHighlighted:Boolean = true;
		public var showSelected:Boolean = true;
		
		public var highlightedColor:uint = 0xffff00;
		public var selectedColor:uint = 0x00ffff;
		
		
		
		public function wmeGradientLegend()
		{
			super();
			
			this.setStyle( "color" , 0xffffff );
			this.setStyle( "fontWeight" , "bold" );
			
			
			var ds:DropShadowFilter = new DropShadowFilter();
			
			this.minLabel.setStyle( "left" , 0 );
			this.maxLabel.setStyle( "right" , 0 );
			this.minLabel.setStyle( "verticalCenter" , 0 );
			this.maxLabel.setStyle( "verticalCenter" , 0 );
			this.minLabel.filters = [ ds ];
			this.maxLabel.filters = [ ds ];
			
			this.addChild( this.minLabel );
			this.addChild( this.maxLabel );
		}
		

		public function get layer() : wmeGraphicsLayer {
			return this._layer;
		} 
		public function set layer( value:wmeGraphicsLayer ) : void {
			
			if( value != this._layer ){
				if( this._layer ){
					this._layer.selected.removeEventListener( CollectionEvent.COLLECTION_CHANGE , onLayerChange );
					this._layer.highlighted.removeEventListener( CollectionEvent.COLLECTION_CHANGE , onLayerChange );
					this._layer = null;
				}
				
				if( value.renderer is wmeRenderer && (value.renderer as wmeRenderer).innerRenderer is wmeGradientRenderer ){
					this._layer = value;
				}
				
				if( this._layer ){
					this._layer.selected.addEventListener( CollectionEvent.COLLECTION_CHANGE , onLayerChange );
					this._layer.highlighted.addEventListener( CollectionEvent.COLLECTION_CHANGE , onLayerChange );
				}
				
				this.refresh();
			}
		}
		
		public function get visibleRange() : MinMax {
			if( this._visibleRange && this._visibleRange.isValid ){
				return this._visibleRange;
			}
			
			if( this._layer ){
				var renderer:wmeGradientRenderer = (this._layer.renderer as wmeRenderer).innerRenderer as wmeGradientRenderer;
				return new MinMax( renderer.infos[0].value , renderer.infos[renderer.infos.length-1].value );
			}
			else{
				return new MinMax( 0 , 1 );
			}
		}
		public function set visibleRange( mm:MinMax ) : void {
			if( mm != this._visibleRange ){
				this._visibleRange = mm;
				this.refresh();
			}
		}
		
		
		
		
		override protected function commitProperties():void {
			if( this._refreshRequested ){
				this._refreshRequested = false;
				
				if( this._layer ){
					var renderer:wmeGradientRenderer = (this._layer.renderer as wmeRenderer).innerRenderer as wmeGradientRenderer;
					var attribute:String = renderer.attribute;
					var infos:Array = renderer.infos;
					var length:int = infos.length;
					infos.sortOn( "value" , Array.NUMERIC );
					
					var value:Number;
					var pct:Number;
					
					//reset labels
					var vRange:MinMax = this.visibleRange;
					var f:Object = ( this.isCurrency ) ? new CurrencyFormatter() : new NumberFormatter();
					f.useThousandsSeparator = true;
					
					if( this.isPercentage ){
						this.minLabel.text = f.format( vRange.min * 100 ) + "%";
						this.maxLabel.text = f.format( vRange.max * 100 ) + "%";
					}
					else{
						this.minLabel.text = f.format( vRange.min );
						this.maxLabel.text = f.format( vRange.max );
					}
					
					
					
					var values:Array = new Array();
					//add values of range end points
					values.push( vRange.min );
					values.push( vRange.max );
					
					//add all the values from infos that are inside of range
					for( var i:int = 0; i < length; i++ ){
						var info:wmeGradientInfo = infos[i] as wmeGradientInfo;
						value = info.value;
						
						if( value > vRange.min && value < vRange.max ){
							values.push( value );
						}
					}
					
					//add extra values for log scale
					var logLines:Array = new Array();
					if( renderer.calculatePercentageMethod == wmeGradientRenderer.calcPctLog ){
						var logMin:int = int( Math.log( vRange.min ) + 1 );
						var logMax:Number = int( Math.log( vRange.max ) + 1 );
						
						for( var j:int = logMin; j < logMax; j++ ){
							var eValue:Number = Math.exp( j );
							values.push( eValue );
							logLines.push( eValue );
						}
					}
					
					values.sort( Array.NUMERIC );
					
					try{
						//setup gradient arrays
						var colors:Array = new Array();
						var alphas:Array = new Array();
						var ratios:Array = new Array();
						var m:Matrix = new Matrix();
						m.createGradientBox( this.width , this.height , this.rotation , 0 , 0 );
						
						length = values.length;
						for( var k:int = 0; k < length; k++ ){
							value = values[k];
							pct = ( value - vRange.min ) / vRange.width; 
							
							var sym:Object = renderer.GetSymbolFromData( values[k] );
							
							colors.push( sym.color );
							alphas.push( sym.alpha );
							ratios.push( 255 * pct );
						}
						
						//draw gradient
						this.graphics.clear();
						this.graphics.lineStyle( 2 , 0xffffff , 1 , true );
						this.graphics.beginGradientFill( GradientType.LINEAR , colors , alphas , ratios , m , SpreadMethod.PAD , InterpolationMethod.LINEAR_RGB , 0 ); 
						this.graphics.drawRoundRect( 0 , 0 , this.width , this.height , 5 , 5 );
						this.graphics.endFill();
						
						
						//draw log lines
						if( renderer.calculatePercentageMethod == wmeGradientRenderer.calcPctLog ){
							length = logLines.length;
							for( var l:int = 0; l < length; l++ ){
								value = logLines[l];
								pct = ( value - vRange.min ) / vRange.width;
								this.drawVerticalLine( pct , 0x0 , .25 , 1 );
							}						
							
						}
						
						//draw all highlighted items
						
						if( this.showHighlighted ){
							length = this._layer.highlighted.length;
							for( var h:int = 0; h < length; h++ ){
								value = this._layer.highlighted[h].attributes[attribute];
								pct = ( value - vRange.min ) / vRange.width; 
								this.drawVerticalLine( pct , this.highlightedColor , 1 , 3 );
							}
						}
						
						//draw all selected items
						if( this.showSelected ){
							length = this._layer.selected.length;
							for( var s:int = 0; s < length; s++ ){
								value = this._layer.selected[s].attributes[attribute];
								pct = ( value - vRange.min ) / vRange.width; 
								this.drawVerticalLine( pct , this.selectedColor , 1 , 3 );
							}					
						}
						
					}
					catch( error:Object ){
						this.minLabel.text = "Error";
						this.maxLabel.text = error.toString();
					}
				}

			}
		} 
		
	
		public function refresh() : void {
			this._refreshRequested = true;
			this.invalidateProperties();
		}
		
		protected function onLayerChange( event:CollectionEvent ) : void {
			this.refresh();
		}
		
		protected function calculateColor( pct:Number , min:int , max:int ) : uint {
			var minColor:Object = { color : min , r : (min & 0xff0000) >> 16 , g : (min & 0xff00) >> 8 , b : (min & 0xff) };
			var maxColor:Object = { color : max , r : (max & 0xff0000) >> 16 , g : (max & 0xff00) >> 8 , b : (max & 0xff) };
			
			var differences:Object = new Object();
			differences.r = maxColor.r - minColor.r;
			differences.g = maxColor.g - minColor.g;
			differences.b = maxColor.b - minColor.b;
			
			var r:int = int((Number(differences.r) * Number(pct))+ Number(minColor.r));
			var g:int = int((Number(differences.g) * Number(pct))+ Number(minColor.g));
			var b:int = int((Number(differences.b) * Number(pct))+ Number(minColor.b));
			
			var color:uint = (r<<16)+(g<<8)+b;
			return color;
		}
		
		
		protected function drawVerticalLine( pct:Number , color:uint , alpha:Number , thickness:Number ) : void {
			this.graphics.lineStyle( thickness , color , alpha , true );
			
			var x:Number = this.width * pct;
			x = ( this.width - x < 3 ) ? this.width - 3 : x;
			x = ( x < 3 ) ? 3 : x;
			
			this.graphics.moveTo( x , 1 );
			this.graphics.lineTo( x , this.height - 2 );
		}
		
		
		
	}
}